package edu.gatech.app4args.utils;

import edu.gatech.app4args.resources.ResourceLoader;

import java.io.File;
import java.io.IOException;
import java.math.BigInteger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import org.docx4j.XmlUtils;
import org.docx4j.convert.out.flatOpcXml.FlatOpcXmlCreator;
import org.docx4j.jaxb.Context;
import org.docx4j.jaxb.NamespacePrefixMapperUtils;
import org.docx4j.model.table.TblFactory;
import org.docx4j.openpackaging.contenttype.ContentType;
import org.docx4j.openpackaging.exceptions.Docx4JException;
import org.docx4j.openpackaging.exceptions.InvalidFormatException;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.PartName;
import org.docx4j.openpackaging.parts.WordprocessingML.AlternativeFormatInputPart;
import org.docx4j.openpackaging.parts.WordprocessingML.NumberingDefinitionsPart;
import org.docx4j.relationships.Relationship;
import org.docx4j.wml.CTAltChunk;
import org.docx4j.wml.Numbering;
import org.docx4j.wml.P;
import org.docx4j.wml.PPr;
import org.docx4j.wml.PPrBase.NumPr;
import org.docx4j.wml.PPrBase.NumPr.Ilvl;
import org.docx4j.wml.PPrBase.NumPr.NumId;
import org.docx4j.wml.R;
import org.docx4j.wml.Tbl;

/**
 * Unused in current version, therefore javadoc will only be done if time permits.
 *
 * @author Andrew
 */
public class DocxCreator extends FileExporter {

	private WordprocessingMLPackage wordMLPackage;
	private static org.docx4j.wml.ObjectFactory factory;
	
	public DocxCreator(){
		super();
		// init objects
		String numberingXML;
		factory = new org.docx4j.wml.ObjectFactory();

		try {
			numberingXML = ResourceLoader.slideLayoutFile("numbering.xml");
		} catch (IOException e) {
			System.err.println("Error: File read");
			e.printStackTrace();
			numberingXML = defaultNumbering;
		}
		try {
			wordMLPackage = WordprocessingMLPackage.createPackage();
			NumberingDefinitionsPart ndp = new NumberingDefinitionsPart();
			wordMLPackage.getMainDocumentPart().addTargetPart(ndp);
			ndp.setJaxbElement( (Numbering) XmlUtils.unmarshalString(numberingXML));
		} catch (InvalidFormatException e) {
			System.err.println("Error: wordMLPackage");
			e.printStackTrace();
		} catch (JAXBException e) {
			System.err.println("Error: JAXB");
			e.printStackTrace();
		}	
	}
	
	public void oldTestDocx(){
		// TODO replace with create paragraph
		wordMLPackage.getMainDocumentPart().addStyledParagraphOfText("Title", "Hello world refactored bitches");
		wordMLPackage.getMainDocumentPart().addParagraphOfText("from docx4j!");
		
		// numbering
		wordMLPackage.getMainDocumentPart().addObject( createListParagraph(1, 0, "text on top level" ));
		wordMLPackage.getMainDocumentPart().addObject( createListParagraph(1, 1, "text on lower level" ));
		wordMLPackage.getMainDocumentPart().addObject( createListParagraph(1, 1, "more text on lower level" ));
		// bullets
		wordMLPackage.getMainDocumentPart().addObject( createListParagraph(2, 0, "bulleted bullshit text on top level" ));
		wordMLPackage.getMainDocumentPart().addObject( createListParagraph(2, 1, "bulleted text on lower level" ));

	}

	public void testDocx(){
		// title and regular paragraphs
		addParagraph("Title", "New methods & such");
		addParagraph("just normal old text here, nothing to see");
		addParagraph("Lorem ipsum dolor sit amet, consectetur adipiscing elit. In quis sapien ligula, nec faucibus quam. Cras hendrerit volutpat ligula, eu elementum nunc feugiat eget. Morbi dapibus sem at nibh blandit non varius velit placerat. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nulla luctus vehicula ultricies. Proin viverra velit ac est pulvinar ultrices. Suspendisse nulla lectus, mattis id luctus quis, lacinia et magna. Pellentesque nulla augue, vehicula id imperdiet id, fermentum in metus. Aliquam ac ligula vitae lectus eleifend venenatis. Praesent mollis lobortis hendrerit.");
		addParagraph("Cras luctus mattis erat, laoreet tincidunt sapien convallis eu. Nullam non urna orci, euismod porttitor purus. Nullam egestas, elit in scelerisque tincidunt, nisl nulla pharetra erat, eget feugiat felis arcu non elit. Morbi hendrerit ornare est, at aliquet enim viverra eu. Suspendisse vel lorem a enim feugiat sagittis quis non enim. Morbi at viverra tellus. Mauris blandit vulputate eros, in aliquet turpis tempor vitae. Fusce ultricies, erat et dapibus pulvinar, felis ante facilisis dolor, vel sagittis nunc diam sit amet nisi. Nunc eget quam nulla. Praesent urna quam, ultricies posuere hendrerit vitae, vestibulum vel enim. Duis eget ipsum nulla, non luctus justo. Aenean venenatis dignissim arcu, id consectetur nunc sodales vitae. In lobortis accumsan porta. Nunc ultrices interdum euismod. Phasellus accumsan augue nec dolor semper aliquam. Donec et urna risus. Sed in enim dui, non tempus nulla. Sed at augue risus, sed vehicula felis.");
		// bulleted lists
		addListParagraph(bulleted, 0, "Bullet #1 better fucking be a bullet");
		addListParagraph(bulleted, 0, "This should also be a bullet");
		addListParagraph(bulleted, 1, "..and indented in once");
		// more text to make the argument
		addParagraph("Nulla venenatis ipsum ac elit ullamcorper rhoncus. Duis tempor lacinia est nec congue. Duis sagittis scelerisque venenatis. Integer accumsan eros quis nisl eleifend tincidunt. Pellentesque gravida ultrices aliquam. Nunc dictum mollis pellentesque. Mauris convallis feugiat volutpat. Phasellus facilisis elit pretium nisl placerat commodo. Donec fringilla ultrices dictum. Nunc vel ligula id lacus mollis rutrum a nec dolor. Quisque nec scelerisque sem. Maecenas in odio at erat tincidunt iaculis vitae nec neque. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Integer faucibus arcu et sapien vestibulum nec congue felis dapibus. In nunc augue, bibendum ut molestie sit amet, consequat in justo. Vivamus arcu ipsum, lacinia eu rhoncus sed, aliquam sit amet neque.");
		// numbered lists
		addListParagraph(numbered, 0, "Reason #1");
		addListParagraph(numbered, 0, "Reason #2");
		addListParagraph(numbered, 1, "With supporting evidence!");
		addListParagraph(numbered, 1, "And even more truthicles!");
		
		exportFile(System.getProperty("user.dir") + "/test_file.docx");
	}
	
	@SuppressWarnings("unused")
	private void misc(){
		// To get bold text, you must set the run's rPr@w:b,
		// so you can't use the createParagraphOfText convenience method
		//org.docx4j.wml.P p = wordMLPackage.getMainDocumentPart().createParagraphOfText("text");
		// Here is an easier way:
		String str = "<w:p xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" ><w:r><w:rPr><w:b /></w:rPr><w:t>Bold, just at w:r level</w:t></w:r></w:p>";

		try {
			wordMLPackage.getMainDocumentPart().addObject(org.docx4j.XmlUtils.unmarshalString(str) );
		} catch (JAXBException e) {
			System.err.println("Error: unmarshalling");
			e.printStackTrace();
		}

		// Let's add a table
		int writableWidthTwips = wordMLPackage.getDocumentModel().getSections().get(0).getPageDimensions().getWritableWidthTwips();
		int cols = 3;
		int cellWidthTwips = new Double( 
				Math.floor( (writableWidthTwips/cols ))
		).intValue();

		Tbl tbl = TblFactory.createTable(3, 3, cellWidthTwips);
		wordMLPackage.getMainDocumentPart().addObject(tbl);


		// Add an altChunk
		// .. the part
		try{
			String html = "<html><head><title>Import me</title></head><body><p>Hello World!</p></body></html>";
			AlternativeFormatInputPart afiPart = new AlternativeFormatInputPart(new PartName("/hw.html") ); 
			afiPart.setBinaryData(html.getBytes());
			afiPart.setContentType(new ContentType("text/html"));
			Relationship altChunkRel = wordMLPackage.getMainDocumentPart().addTargetPart(afiPart);
			// .. the bit in document body
			CTAltChunk ac = Context.getWmlObjectFactory().createCTAltChunk();
			ac.setId(altChunkRel.getId());
			// add to document
			wordMLPackage.getMainDocumentPart().addObject(ac);
			// .. content type
			wordMLPackage.getContentTypeManager().addDefaultContentType("html", "text/html");
		} catch (InvalidFormatException e) {
			System.err.println("Error: altChunk");
			e.printStackTrace();
		}

		//injectDocPropsCustomPart(wordMLPackage);
	}
	
	private void addToMain(Object o){
		wordMLPackage.getMainDocumentPart().addObject(o);
	}
	
	private R createRun(String str){
		// create text and add str
		org.docx4j.wml.Text  t = factory.createText();
		t.setValue(str);
		// create run and add text
		org.docx4j.wml.R  run = factory.createR();
		run.getRunContent().add(t);
		return run;
	}
	
	private void addRun(P p, String str){
		R run = createRun(str);
		// add run to p
		p.getParagraphContent().add(run);
	}
	@SuppressWarnings("unused")
	private void addRunBold(P p, String str){
		R run = createRun(str);
		// set bold
		org.docx4j.wml.RPr rpr = factory.createRPr();
		org.docx4j.wml.BooleanDefaultTrue b = new org.docx4j.wml.BooleanDefaultTrue();
		b.setVal(true);	    
		rpr.setB(b);
		run.setRPr(rpr);
		// add to p
		p.getParagraphContent().add(run);
	}
	@SuppressWarnings("unused")
	private P createParagraph(){
		return createParagraph("Normal");
	}
	
	private P createParagraph(String styleId){
		org.docx4j.wml.P  p = factory.createP();
		// assume styleId is valid
		PPr  pPr = factory.createPPr();
		p.setPPr(pPr);
		org.docx4j.wml.PPrBase.PStyle pStyle = factory.createPPrBasePStyle();
		pPr.setPStyle(pStyle);
		pStyle.setVal(styleId);
		return p;
	}
	
	// TODO change to use array to build paragraph from sentences
	private void addParagraph(String para){
		addParagraph("Normal", para);
	}
	
	// TODO change to use array
	private void addParagraph(String styleId, String para){
		P p = createParagraph(styleId);
		// loop array of strings
		addRun(p, para);

		// add to package
		addToMain(p);
	}
	
	
	private P createListParagraph(long numId, long ilvl, String para ) {
		
		P p = createParagraph("ListParagraph");
		addRun(p, para);
		
		// Create and add <w:numPr>
		NumPr numPr =  factory.createPPrBaseNumPr();
		p.getPPr().setNumPr(numPr);

		// The <w:ilvl> element
		Ilvl ilvlElement = factory.createPPrBaseNumPrIlvl();
		numPr.setIlvl(ilvlElement);
		ilvlElement.setVal(BigInteger.valueOf(ilvl));

		// The <w:numId> element
		NumId numIdElement = factory.createPPrBaseNumPrNumId();
		numPr.setNumId(numIdElement);
		numIdElement.setVal(BigInteger.valueOf(numId));

		return p;

	}
	
	// TODO use array for input
	private void addListParagraph(long numId, long ilvl, String para){
		P p = createListParagraph(numId, ilvl, para);
		addToMain(p);
	}
	
	public void exportFile(String filepath) {
		// TODO pull from variable or eventually gui
		if(filepath == null){
			write(System.getProperty("user.dir") + "/Exported_Document.docx");
		} else {
			write(filepath);
		}
	}
	
	// TODO maybe move to exportFile()
	private void write(String outputFile){
		// TODO fix this to read correct path
		boolean save = (outputFile == null ? false : true);
		// Now save it
		if (save) {
			try {
				wordMLPackage.save(new File(outputFile) );
			} catch (Docx4JException e) {
				System.err.println("Error: wordMLPackage");
				e.printStackTrace();
			}
			System.out.println("Saved " + outputFile);
		} else {
			try {
				// Create a org.docx4j.wml.Package object
				FlatOpcXmlCreator worker = new FlatOpcXmlCreator(wordMLPackage);
				org.docx4j.xmlPackage.Package pkg = worker.get();
				// Now marshall it
				JAXBContext jc = Context.jcXmlPackage;
				Marshaller marshaller = jc.createMarshaller();
				marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
				NamespacePrefixMapperUtils.setProperty(marshaller, 
						NamespacePrefixMapperUtils.getPrefixMapper());			
				System.out.println( "\n\n OUTPUT " );
				System.out.println( "====== \n\n " );	
				marshaller.marshal(pkg, System.out);
			} catch (Docx4JException e) {
				System.err.println("Error: Docx4JException");
				e.printStackTrace();
			} catch (JAXBException e) {
				System.err.println("Error: JAXBException");
				e.printStackTrace();
			}
		}
	}
}
